/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package jwu2.indexserver;

import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jwu2.addondb.AddonDB;
import jwu2.addondb.AddonDBEntry;
import jwu2.core.Addon;
import jwu2.core.Config;
import jwu2.core.Site;
import jwu2.log.Logger;
import jwu2.net.ConnectionFactory;
import lombok.Cleanup;

/**
 *
 * @author Rolf
 */
public class IndexServerFetcher {
    
    private static boolean DEBUG_FETCH_ENABLED = false;

    protected long updateWaitTime;
    protected long lastUpdateTimestamp;
    
    protected Site addonSite;
    
    private int totalPagesMultipleURL;
    
    public IndexServerFetcher(Site s) {
        addonSite = s;
        try {
            lastUpdateTimestamp = Long.parseLong(Config.singleton().getValue("lastFetchUpdate-"+s.getId()));
        }
        catch(Exception e) {
            lastUpdateTimestamp = 0;
        }
        totalPagesMultipleURL = Integer.MAX_VALUE;
        updateWaitTime = 6 * 60 * 60 * 1000; //Update once / day
    }
    
    public Site getSite() {
        return addonSite;
    }
    
    public boolean shouldFetch() {
        long now = System.currentTimeMillis();
        
        return lastUpdateTimestamp+updateWaitTime < now;
    }
    
    public List<Addon> fetchAddons() {
        List<Addon> addons;
        try {
            Logger.logln("New fetching full addon list from site: "+addonSite);
            addons = fetchFullAddonList();
        }
        catch(Exception e) {
            addons = null;
        }

        lastUpdateTimestamp = System.currentTimeMillis();
        Config.singleton().setValue("lastFetchUpdate-"+addonSite.getId(), ""+lastUpdateTimestamp);
        
        if( addons != null ) {
            
            //Successfull update, wait 1day for next update
            updateWaitTime = 24 * 60 * 60 * 1000;
            
            Logger.logln("Sucessfully updated addons for site ["+this.getSite()+"]");
        }
        else {
            //Unsuccessfull update, try again in 10min
            updateWaitTime = 10 * 60 * 1000;
            
            Logger.logln("Error fetching addons for site ["+this.getSite()+"], will retry in 10min");
        }
        
        return addons;
    }
    
    protected List<Addon> fetchFullAddonList() {
        if( addonSite.isMultipleFetchUrl() ) {
            return fetchFullAddonListMultiple();
        }
        else {
            return fetchFullAddonListNonMultiple(addonSite.getFetchURL());
        }
    }
    
    protected List<Addon> fetchFullAddonListMultiple() {
        
        long latestAddonInDB = findLatestAddonInDB(addonSite);
        
        List<Addon> addonsFound = new ArrayList<Addon>();
        
        int multiFetchID = addonSite.getBeginFetchID();
        
        boolean reachedOlderAddon = false;
        
        while(multiFetchID <= totalPagesMultipleURL) {
            String url = addonSite.getFetchURL().replaceAll("%pageid%", ""+multiFetchID);

            Logger.logln("Now fetching page "+multiFetchID+" / "+totalPagesMultipleURL);

            List<Addon> fetchedAddons = fetchFullAddonListNonMultiple(url);

            addonsFound.addAll(fetchedAddons);
            
            Logger.logln("Found "+ fetchedAddons.size() +" addons on page "+multiFetchID+" ("+addonsFound.size()+" total)");

            if( addonSite.shouldStopOnDate() ) {
                for(Addon a : fetchedAddons) {
                    if(a.getDate() < latestAddonInDB) {
                        //Found addon older than from last check
                        //Must have reached end of "new" addons
                        
                        reachedOlderAddon = true;
                    }
                }
                if( reachedOlderAddon ) {
                    Logger.logln("Reached addon older than what was fetched last, stopping");
                        
                    break;
                }
            }

            multiFetchID++;
        }
        
        return addonsFound;
    }
    
    protected List<Addon> fetchFullAddonListNonMultiple(String url) {
        Map<Site.Patterns, String> patterns = addonSite.getFetchPatterns();
        List<Addon> addonsFound = new ArrayList<Addon>();
        
        try {
            @Cleanup BufferedReader reader = ConnectionFactory.getReaderFromURLNoCache(url);

            Pattern addonStart = Pattern.compile(patterns.get(Site.Patterns.ADDONSTART));
            Pattern addonEnd = Pattern.compile(patterns.get(Site.Patterns.ADDONEND));
            Pattern addonName = Pattern.compile(patterns.get(Site.Patterns.NAME));
            Pattern addonAuthor = Pattern.compile(patterns.get(Site.Patterns.AUTHOR));
            Pattern addonDescription = Pattern.compile(patterns.get(Site.Patterns.DESCRIPTION));
            Pattern addonDownloadUrl = Pattern.compile(patterns.get(Site.Patterns.DOWNLOADURL));
            Pattern addonEndOfPage = Pattern.compile(patterns.get(Site.Patterns.ENDOFPAGE));
            Pattern addonID = Pattern.compile(patterns.get(Site.Patterns.ID));
            Pattern addonDate = Pattern.compile(patterns.get(Site.Patterns.DATE));

            Matcher m;
            Addon newAddon = null;
            String line;

            line = reader.readLine();
        
            while(line != null) {
                line = line.trim();
                
                //Fix up some html encoding
                line = line.replaceAll("&#039;", "'");
                line = line.replaceAll("&amp;", "&");

                if(DEBUG_FETCH_ENABLED)
                System.out.println("["+line+"]");

                //AddonEnd (New addon)
                m = addonEnd.matcher(line);
                if( m.matches() ) {
                    //End of addon data

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon End****************");

                    if( newAddon != null) {

                        if( !newAddon.getId().equals("") || !newAddon.getName().equals("") ) {
                            addonsFound.add(newAddon);
                        }
                        newAddon = null;
                    }
                }

                //AddonStart (New addon)
                m = addonStart.matcher(line);
                if( m.matches() ) {
                    //New addon found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon Start****************");

                    newAddon = new Addon("");
                    newAddon.setDownloadUrl("");
                    newAddon.setId("");
                }
                
                //AddonName
                m = addonName.matcher(line);
                if( m.matches() ) {
                    //Name found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon Name****************");

                    if(newAddon != null) {
                        newAddon.setValue("name", m.group(1));
                    }
                }
                
                //AddonAuthor
                m = addonAuthor.matcher(line);
                if( m.matches() ) {
                    //Author found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon Author****************");

                    if(newAddon != null) {
                        newAddon.setValue("author", m.group(1));
                    }
                }

                //AddonDescription
                m = addonDescription.matcher(line);
                if( m.matches() ) {
                    //Description found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon Description****************");

                    if(newAddon != null) {
                        newAddon.setValue("description", m.group(1));
                    }
                }

                //AddonID
                m = addonID.matcher(line);
                if( m.matches() ) {
                    //ID found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon ID****************");

                    if(newAddon != null) {
                        newAddon.setId(m.group(1));
                    }
                }

                //AddonDownloadURL
                m = addonDownloadUrl.matcher(line);
                if( m.matches() ) {
                    //Download url found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon Download URL****************");

                    if(newAddon != null) {
                        newAddon.setDownloadUrl(m.group(1));
                    }
                }

                //Addon Date
                m = addonDate.matcher(line);
                if( m.matches() ) {
                    //Date found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************Addon Date****************");

                    if(newAddon != null) {
                        String date = "";
                        
                        //MM DD YYYY
                        
                        String year = m.group(3);
                        String month = m.group(1);
                        String day = m.group(2);

                        if(addonSite.isDateHasDayAndMonthSwapped()) {
                            String tmp = month;
                            month = day;
                            day = tmp;
                        }

                        try {
                            Integer.parseInt(month);
                        }
                        catch(Exception e) {
                            month = ""+convertMonthFromString(month);
                        }

                        if( day.length() == 1) {
                            day = "0"+day;
                        }
                        
                        if( month.length() == 1) {
                            month = "0"+month;
                        }

                        String dateString = year+month+day;

                        if(addonSite.isDateHasTime()) {
                            String hour = m.group(4);
                            String minutes = m.group(5);

                            if(addonSite.isDateAMPM()) {
                                String ampm = m.group(6);

                                if(ampm.equalsIgnoreCase("pm")) {
                                    hour = (Integer.parseInt(hour)+12)+"";
                                }
                            }

                            if( hour.length() == 1) {
                                hour = "0"+hour;
                            }

                            if( minutes.length() == 1) {
                                minutes = "0"+minutes;
                            }

                            dateString += hour+minutes;
                        }

                        newAddon.setDate(Long.parseLong(dateString));
                    }
                }

                //AddonEndOfPage
                m = addonEndOfPage.matcher(line);
                if( m.matches() ) {
                    //End of page found

                    if(DEBUG_FETCH_ENABLED)
                    System.out.println("***************End of Page****************");

                    break;
                }

                if( addonSite.isMultipleFetchUrl() ) {
                    Pattern addonTotalPages = Pattern.compile(patterns.get(Site.Patterns.ENDMULTIPLEFETCH));
                    
                    //AddonMultipleFetchTotalPages
                    m = addonTotalPages.matcher(line);
                    if( m.matches() ) {
                        //Found total amount of pages number

                        if(DEBUG_FETCH_ENABLED)
                        System.out.println("***************Total Pages****************");

                        totalPagesMultipleURL = Integer.parseInt(m.group(1));
                    }
                    
                }
                
                line = reader.readLine();
            }
        }
        catch (Exception ex) {
            Logger.logln("Error fetching addonlist from url ["+url+"]: "+ex);
            
            return null;
        }
        
        return addonsFound;
    }
    
    private long findLatestAddonInDB(Site addonSite) {
        long date = 0;
        List<AddonDBEntry> addons = AddonDB.singleton().getFullAddonList();
        
        for(AddonDBEntry adbe : addons) {
            Addon a = adbe.getAddonFromSite(addonSite);
            
            if(a != null) {
                long t = a.getDate();
                
                date = Math.max(date, t);
            }
        }
        
        return date;
    }

    private int convertMonthFromString(String month) {
        if(month.equalsIgnoreCase("jan")) {
            return 1;
        }
        else if(month.equalsIgnoreCase("feb")) {
            return 2;
        }
        else if(month.equalsIgnoreCase("mar")) {
            return 3;
        }
        else if(month.equalsIgnoreCase("apr")) {
            return 4;
        }
        else if(month.equalsIgnoreCase("may")) {
            return 5;
        }
        else if(month.equalsIgnoreCase("jun")) {
            return 6;
        }
        else if(month.equalsIgnoreCase("jul")) {
            return 7;
        }
        else if(month.equalsIgnoreCase("aug")) {
            return 8;
        }
        else if(month.equalsIgnoreCase("sep")) {
            return 9;
        }
        else if(month.equalsIgnoreCase("oct")) {
            return 10;
        }
        else if(month.equalsIgnoreCase("nov")) {
            return 11;
        }
        else if(month.equalsIgnoreCase("dec")) {
            return 12;
        }

        System.out.println("Unknown month: ["+month+"]");

        return -1;
    }
}
